/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "http2_napi.h"
#include "http2_common.h"
#include "napi/http2_utils.h"
#include <uv.h>

static napi_env envMessage_ = nullptr;
static napi_ref callBackRefMessage_ = nullptr;


void onEventListener(int type, void *data, int len, int reqType) {
    void *swapData = nullptr;
    char *responseHeaders = nullptr;
    uint8_t *responseDataTrunck = nullptr;
    int error_code = 0;
    int last_stream_id = 0;
    int fd = 0;
    if (type == HTTPSTREAM_CALLBACK_TYPE_REPONSE || type == HTTPSTREAM_CALLBACK_TYPE_ERROR ||
        type == HTTPSTREAM_CALLBACK_TYPE_CLOSE) {
        responseHeaders = (char *)malloc(strlen((char *)data) + 1);
        memcpy(responseHeaders, (char *)data, strlen((char *)data) + 1);
        swapData = responseHeaders;
    }
    if (type == HTTPSTREAM_CALLBACK_TYPE_DATA_TRUNCK) {
        responseDataTrunck = (uint8_t *)malloc(len + 1);
        memcpy(responseDataTrunck, (uint8_t *)data, len + 1);
        swapData = responseDataTrunck;
    }
    if (type == HTTPSTREAM_CALLBACK_TYPE_GOWAY) {
        std::unordered_map<char *, void *> *gowayMap = static_cast<std::unordered_map<char *, void *> *>(data);
        uint8_t *opaqueData = nullptr;
        for (auto it = gowayMap->begin(); it != gowayMap->end(); it++) {
            if (it->first == "opaque_data") {
                if (it->second) {
                    opaqueData = (uint8_t *)malloc(strlen((char *)it->second) + 1);
                    memcpy(opaqueData, (uint8_t *)it->second, strlen((char *)data) + 1);
                    swapData = opaqueData;
                }
            }
            if (it->first == "error_code") {
                error_code = (int)(intptr_t)(reinterpret_cast<int *>(it->second));
            }
            if (it->first == "last_stream_id") {
                last_stream_id = (int)(intptr_t)(reinterpret_cast<int *>(it->second));
            }
            if (it->first == "fd") {
                fd = (int)(intptr_t)(reinterpret_cast<int *>(it->second));
            }
        }
    }
    RequestCallback *requestCallback = new RequestCallback{.env = envMessage_,
                                                           .callbackRef = callBackRefMessage_,
                                                           .type = type,
                                                           .error_code = error_code,
                                                           .fd = fd,
                                                           .last_stream_id = last_stream_id,
                                                           .data = swapData,
                                                           .data_len = len,
                                                           .reqType = reqType};
    uv_loop_s *loopMessage = nullptr;
    napi_get_uv_event_loop(envMessage_, &loopMessage);
    if (loopMessage == nullptr) {
        LOGE("napi-->loopMessage null");
        return;
    }
    uv_work_t *work = new (std::nothrow) uv_work_t;
    if (work == nullptr) {
        LOGE("napi-->work null");
        return;
    }
    work->data = requestCallback;
    uv_queue_work(
        loopMessage, work, [](uv_work_t *work) {},
        [](uv_work_t *work, int status) {
            LOGI("napi-->uv_queue_work");
            RequestCallback *requestCallback = (RequestCallback *)(work->data);
            napi_value typeNV;
            napi_value dataNV;
            napi_create_int32(requestCallback->env, requestCallback->type, &typeNV);
            if (requestCallback->type == HTTPSTREAM_CALLBACK_TYPE_REPONSE ||
                requestCallback->type == HTTPSTREAM_CALLBACK_TYPE_ERROR ||
                requestCallback->type == HTTPSTREAM_CALLBACK_TYPE_CLOSE) {
                LOGE("HTTPSTREAM_CALLBACK_TYPE_REPONSE value %s", requestCallback->data);
                napi_create_string_utf8(requestCallback->env, (char *)requestCallback->data, NAPI_AUTO_LENGTH, &dataNV);
            }
            if (requestCallback->type == HTTPSTREAM_CALLBACK_TYPE_DATA_TRUNCK) {
                LOGE("HTTPSTREAM_CALLBACK_TYPE_DATA_TRUNCK len %d", requestCallback->data_len);
                // TODO reqType=1 grpc服务端响应数据需要裁剪 回调过来的数据前几位buffer异常
                if (requestCallback->reqType == 1 && requestCallback->data_len >= 8) {
                    uint8_t *tempData = (uint8_t *)requestCallback->data;
                    auto handleBuf = checkBufferNum(tempData, requestCallback->data_len);
                    size_t start_index = handleBuf.dirty_data_Len;
                    size_t copy_length = handleBuf.effective_data_len;
                    LOGE("HTTPSTREAM_CALLBACK_TYPE_DATA_TRUNCK dirty_data_Len %d effective_data_len %d", start_index,
                         copy_length);
                    uint8_t *clippedData = (uint8_t *)malloc(copy_length);
                    std::copy(tempData + start_index, tempData + start_index + copy_length, clippedData);
                    createArrayBuffer(requestCallback->env, clippedData, requestCallback->data_len - 7, &dataNV);
                    if (clippedData) {
                        free(clippedData);
                        clippedData = nullptr;
                    }
                } else {
                    createArrayBuffer(requestCallback->env, requestCallback->data, requestCallback->data_len, &dataNV);
                }
            }
            if (requestCallback->type == HTTPSTREAM_CALLBACK_TYPE_GOWAY) {
                std::string opaqueData = "";
                if (requestCallback->data) {
                    opaqueData = reinterpret_cast<char *>(requestCallback->data);
                }
                nlohmann::json jsonGoWay = nlohmann::json{
                    {"error_code", requestCallback->error_code},
                    {"last_stream_id", requestCallback->last_stream_id},
                    {"fd", requestCallback->fd},
                    {"opaque_data", (char *)opaqueData.c_str()},
                };
                napi_create_string_utf8(requestCallback->env, (char *)jsonGoWay.dump().c_str(), NAPI_AUTO_LENGTH,
                                        &dataNV);
            }
            napi_value ret = 0;
            napi_value callback = nullptr;
            napi_get_reference_value(requestCallback->env, requestCallback->callbackRef, &callback);
            if (dataNV) {
                napi_value argv_2[] = {typeNV, dataNV};
                napi_call_function(requestCallback->env, nullptr, callback, 2, argv_2, &ret);
                if (requestCallback->data && (requestCallback->type != HTTPSTREAM_CALLBACK_TYPE_GOWAY)) {
                    free(requestCallback->data);
                    requestCallback->data = nullptr;
                }
            } else {
                napi_value argv_1[] = {typeNV};
                napi_call_function(requestCallback->env, nullptr, callback, 1, argv_1, &ret);
            }
            if (work != nullptr) {
                delete work;
            }
            delete requestCallback;
            LOGI("napi-->uv_queue_work end");
        });
}

int Http2Napi::ConnectTo(const char *host, uint16_t port) {
    struct addrinfo hints;
    int fd = -1;
    int rv;
    char service[NI_MAXSERV];
    struct addrinfo *res, *rp;
    snprintf(service, sizeof(service), "%u", port);
    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    rv = getaddrinfo(host, service, &hints, &res);
    if (rv != 0) {
        LOGE("getaddrinfo %s", gai_strerror(rv));
        return -1;
    }
    for (rp = res; rp; rp = rp->ai_next) {
        fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if (fd == -1) {
            continue;
        }
        while ((rv = connect(fd, rp->ai_addr, rp->ai_addrlen)) == -1 && errno == EINTR)
            ;
        if (rv == 0) {
            break;
        }
        close(fd);
        fd = -1;
    }
    freeaddrinfo(res);
    sessionManagerMap[fd] = 0;
    return fd;
}

napi_value Http2Napi::ClassConstructor(napi_env env, napi_callback_info info) {
    LOGE("napi-->ClassConstructor");
    napi_value targetObj = nullptr;
    size_t argc = PARAM_COUNT_0;
    napi_value args[PARAM_COUNT_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, &targetObj, nullptr);
    Http2Napi *http2Napi = new Http2Napi();
    napi_wrap(
        env, nullptr, http2Napi,
        [](napi_env env, void *data, void *hint) {
            Http2Napi *http2NapiObj = (Http2Napi *)data;
            delete http2NapiObj;
            http2NapiObj = nullptr;
        },
        nullptr, nullptr);
    return targetObj;
}


napi_value Http2Napi::Connect(napi_env env, napi_callback_info info) {
    LOGE("napi-->Connect");
    size_t argc = PARAM_COUNT_2;
    napi_value args[PARAM_COUNT_2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    std::string host;
    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, host);
    std::string port;
    NapiUtil::JsValueToString(env, args[INDEX_1], STR_DEFAULT_SIZE, port);
    struct sigaction act;
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &act, 0);
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);
    napi_value resourceName;
    napi_create_string_latin1(env, "connect", NAPI_AUTO_LENGTH, &resourceName);
    RequestTask *requestTask =
        new RequestTask{.env = env, .asyncWork = nullptr, .deferred = deferred, .host = host, .port = port};
    napi_create_async_work(
        env, nullptr, resourceName,
        [](napi_env env, void *data) {
            RequestTask *requestTask = (RequestTask *)data;
            LOGE("napi-->Connect start host %s port %s", requestTask->host.c_str(), requestTask->port.c_str());
            int fd = Http2Napi::ConnectTo(requestTask->host.c_str(), NapiUtil::StringToInt(requestTask->port.c_str()));
            LOGE("napi-->Connect fd %d host %s port %s", fd, requestTask->host.c_str(), requestTask->port.c_str());
            requestTask->fd = fd;
            reqManagerMap[fd] = true;
        },
        [](napi_env env, napi_status status, void *data) {
            LOGE("napi-->Connect fd success");
            RequestTask *requestTask = (RequestTask *)data;
            napi_value result;
            napi_create_int32(env, requestTask->fd, &result);
            napi_resolve_deferred(requestTask->env, requestTask->deferred, result);
            napi_delete_async_work(env, requestTask->asyncWork);
            delete requestTask;
        },
        requestTask, &requestTask->asyncWork);
    napi_queue_async_work(env, requestTask->asyncWork);
    return promise;
}


void Http2Napi::setCallBackRefMessage(const napi_ref &callbackRef) { callBackRefMessage_ = callbackRef; }

void Http2Napi::setEnvMessage(const napi_env &env) { envMessage_ = env; }


napi_value Http2Napi::Request(napi_env env, napi_callback_info info) {
    LOGE("napi--->Request");
    isNeedRequest = false;
    size_t argc = PARAM_COUNT_6;
    napi_value args[PARAM_COUNT_6] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    std::string fdStr;
    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, fdStr);
    std::string host;
    NapiUtil::JsValueToString(env, args[INDEX_1], STR_DEFAULT_SIZE, host);
    std::string path;
    NapiUtil::JsValueToString(env, args[INDEX_2], STR_DEFAULT_SIZE, path);
    std::string method;
    NapiUtil::JsValueToString(env, args[INDEX_3], STR_DEFAULT_SIZE, method);
    std::string contentType;
    NapiUtil::JsValueToString(env, args[INDEX_4], STR_DEFAULT_SIZE, contentType);
    int fd = NapiUtil::StringToInt(fdStr);
    if (reqManagerMap.find(fd) == reqManagerMap.end() || !reqManagerMap.at(fd)) {
        return nullptr;
    }
    reqManagerMap[fd] = false;
    LOGE("napi--->Request fd %s host %s path %s method %s contentType %s", fdStr.c_str(), host.c_str(), path.c_str(),
         method.c_str(), contentType.c_str());
    napi_value callback = args[INDEX_5];
    napi_ref callBackRefMessage_;
    napi_create_reference(env, callback, 1, &callBackRefMessage_);
    setEnvMessage(env);
    setCallBackRefMessage(callBackRefMessage_);
    message_callback(onEventListener);
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);
    napi_value resourceName;
    napi_create_string_latin1(env, "request", NAPI_AUTO_LENGTH, &resourceName);

    RequestTask *requestTask = new RequestTask{
        .env = env,
        .asyncWork = nullptr,
        .deferred = deferred,
        .fd = fd,
        .host = host,
        .path = path,
        .method_ = method,
        .contentType = contentType,
    };
    requestManagerMap[fd] = requestTask;
    napi_create_async_work(
        env, nullptr, resourceName,
        [](napi_env env, void *data) {
            usleep(1000 * 200);
            RequestTask *requestTask = (RequestTask *)data;
            int fd = requestTask->fd;
            LOGE("napi-->Request fd :%d", fd);
            struct Connection connection;
            if (requestTask->contentType == "application/grpc") {
                LOGE("napi-->Request application/grpc");
                return;
            }
            SSL_CTX *ssl_ctx;
            SSL *ssl;
            ssl_ctx = SSL_CTX_new(TLS_client_method());
            if (ssl_ctx == NULL) {
                LOGE("SSL_CTX_new", ERR_error_string(ERR_get_error(), NULL));
            }
            init_ssl_ctx(ssl_ctx);
            ssl = SSL_new(ssl_ctx);
            if (ssl == NULL) {
                LOGE("SSL_new", ERR_error_string(ERR_get_error(), NULL));
            }
            int rv = ssl_handshake(ssl, fd, requestTask->host);
            if(rv < 0) {
                LOGE("SSL/TLS handshake failed rv %d",rv);
                return;
            } 
            connection.ssl = ssl;
            connection.ssl_ctx = ssl_ctx;
            connection.want_io = IO_NONE;
            connection.fd = fd;

            make_non_block(fd);
            set_tcp_nodelay(fd);

            LOGE("SSL/TLS handshake completed");

            nghttp2_session_callbacks *callbacks;

            rv = nghttp2_session_callbacks_new(&callbacks);

            if (rv != 0) {
                LOGE("nghttp2_session_callbacks_new %d", rv);
            }

            setup_nghttp2_callbacks(callbacks);

            rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);

            nghttp2_session_callbacks_del(callbacks);

            if (rv != 0) {
                LOGE("nghttp2_session_client_new %d", rv);
            }
            rv = nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
            if (rv != 0) {
                LOGE("nghttp2_submit_settings %d", rv);
            }
            nfds_t npollfds = 1;
            struct pollfd pollfds[1];
            connectManagerMap[connection.fd] = &connection;
            LOGE("napi-->connectManagerMap");
            submit_request(&connection, requestTask->host, requestTask->path, requestTask->method_,
                           requestTask->contentType);

            pollfds[0].fd = requestTask->fd;
            ctl_poll(pollfds, connection.session, connection.want_io);

            while (nghttp2_session_want_read(connection.session) || nghttp2_session_want_write(connection.session)) {
                LOGE("nghttp2_session_want_read");
                if (is_destroyed(fd))
                    break;
                int nfds = poll(pollfds, npollfds, -1);
                if (nfds == -1) {
                    LOGE("poll %s", strerror(errno));
                }
                if (pollfds[0].revents & (POLLIN | POLLOUT)) {
                    int res = handle_io(connection.session, fd);
                    if (res < 0) {
                        break;
                    }
                }
                if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
                    LOGE("nghttp2_session_want_read error");
                    break;
                }
                ctl_poll(pollfds, connection.session, connection.want_io);
            }
            /* Resource cleanup */
            LOGE("Resource cleanup 1");
            if (is_destroyed(fd))
                return;
            LOGE("Resource cleanup go");
            freeManagerMap[fd] = true;
            nghttp2_session_del(connection.session);
            SSL_shutdown(connection.ssl);
            SSL_free(connection.ssl);
            SSL_CTX_free(connection.ssl_ctx);
            shutdown(requestTask->fd, SHUT_WR);
            close(requestTask->fd);
        },
        [](napi_env env, napi_status status, void *data) {
            RequestTask *requestTask = (RequestTask *)data;
            napi_value result;
            napi_create_int32(env, requestTask->fd, &result);
            napi_resolve_deferred(requestTask->env, requestTask->deferred, result);
            napi_delete_async_work(env, requestTask->asyncWork);
            delete requestTask;
        },
        requestTask, &requestTask->asyncWork);
    napi_queue_async_work(env, requestTask->asyncWork);
    return promise;
}


napi_value Http2Napi::Write(napi_env env, napi_callback_info info) {
    LOGE("napi--->Write");
    size_t argc = PARAM_COUNT_2;
    napi_value args[PARAM_COUNT_2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    std::string fdStr;
    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, fdStr);
    void *buffer;
    size_t bufferLen;
    napi_get_arraybuffer_info(env, args[INDEX_1], &buffer, &bufferLen);
    int fd = NapiUtil::StringToInt(fdStr);
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);
    napi_value resourceName;
    napi_create_string_latin1(env, "write", NAPI_AUTO_LENGTH, &resourceName);

    if (requestManagerMap.find(fd) == requestManagerMap.end()) {
        LOGE("Error--->Write not found");
        return promise;
    }
    RequestTask *requestTask = requestManagerMap.at(fd);
    if (requestTask->host.size() <= 0 || requestTask->host.size() > 1000) {
        LOGE("Error--->Write host size < 0");
        return promise;
    }
    RequestTask *requestTask2 = new RequestTask{.env = env,
                                                .asyncWork = nullptr,
                                                .deferred = deferred,
                                                .host = requestTask->host,
                                                .path = requestTask->path,
                                                .buff = buffer,
                                                .fd = fd,
                                                .buffLen = static_cast<int>(bufferLen)};
    napi_create_async_work(
        env, nullptr, resourceName,
        [](napi_env env, void *data) {
            RequestTask *requestTask = (RequestTask *)data;
            int fd = requestTask->fd;
            LOGE("napi-->Write req fd :%d", fd);
            usleep(1000 * 600);
            SSL_CTX *ssl_ctx;
            SSL *ssl;
            struct Connection connection;
            ssl_ctx = SSL_CTX_new(TLS_client_method());
            if (ssl_ctx == NULL) {
                LOGE("SSL_CTX_new %s", ERR_error_string(ERR_get_error(), NULL));
            }
            init_ssl_ctx(ssl_ctx);
            ssl = SSL_new(ssl_ctx);
            if (ssl == NULL) {
                LOGE("SSL_new %s", ERR_error_string(ERR_get_error(), NULL));
            }
            int rv = ssl_handshake(ssl, fd, requestTask->host);
            if(rv < 0) {
                LOGE("SSL/TLS handshake failed rv %d",rv);
                return;
            }
            connection.ssl = ssl;
            connection.ssl_ctx = ssl_ctx;
            connection.want_io = IO_NONE;
            connection.fd = fd;

            make_non_block(fd);
            set_tcp_nodelay(fd);

            LOGE("SSL/TLS handshake completed");

            nghttp2_session_callbacks *callbacks;

            rv = nghttp2_session_callbacks_new(&callbacks);

            if (rv != 0) {
                LOGE("nghttp2_session_callbacks_new %d", rv);
            }

            setup_nghttp2_callbacks(callbacks);

            rv = nghttp2_session_client_new(&connection.session, callbacks, &connection);

            nghttp2_session_callbacks_del(callbacks);

            if (rv != 0) {
                LOGE("nghttp2_session_client_new %d", rv);
            }
            rv = nghttp2_submit_settings(connection.session, NGHTTP2_FLAG_NONE, NULL, 0);
            if (rv != 0) {
                LOGE("nghttp2_submit_settings %d", rv);
            }
            nfds_t npollfds = 1;
            struct pollfd pollfds[1];
            /* Submit the HTTP request to the outbound queue. */
            connection.host = requestTask->host;
            connection.path = requestTask->path;
            connectManagerMap[fd] = &connection;
            LOGE("napi-->connectManagerMap");
            sendData(&connection, requestTask->buff, requestTask->buffLen);

            pollfds[0].fd = requestTask->fd;
            ctl_poll(pollfds, connection.session, connection.want_io);

            /* Event loop */
            while (nghttp2_session_want_read(connection.session) || nghttp2_session_want_write(connection.session)) {
                LOGE("nghttp2_session_want_read");
                if (fd < 0 || is_destroyed(fd))
                    break;
                int nfds = poll(pollfds, npollfds, -1);
                if (nfds == -1) {
                    LOGE("poll %s", strerror(errno));
                }
                if (pollfds[0].revents & (POLLIN | POLLOUT)) {
                    int res = handle_io(connection.session, fd);
                    if (res < 0) {
                        break;
                    }
                }
                if ((pollfds[0].revents & POLLHUP) || (pollfds[0].revents & POLLERR)) {
                    LOGE("nghttp2_session_want_read error");
                    break;
                }
                ctl_poll(pollfds, connection.session, connection.want_io);
            }
            /* Resource cleanup */
            LOGE("Resource cleanup");
            if (is_destroyed(fd))
                return;
            freeManagerMap[fd] = true;
            sleep(1);
            LOGE("Resource cleanup go");
            if (connection.session != NULL) {
                nghttp2_session_del(connection.session);
            }
            if (connection.ssl != NULL) {
                SSL_shutdown(connection.ssl);
                SSL_free(connection.ssl);
            }
            if (connection.ssl_ctx != NULL) {
                SSL_CTX_free(connection.ssl_ctx);
            }
            shutdown(requestTask->fd, SHUT_WR);
            close(requestTask->fd);
        },
        [](napi_env env, napi_status status, void *data) {
            LOGE("napi-->Write fd success");
            RequestTask *requestTask = (RequestTask *)data;
            napi_value result;
            napi_create_int32(env, requestTask->fd, &result);
            napi_resolve_deferred(requestTask->env, requestTask->deferred, result);
            napi_delete_async_work(env, requestTask->asyncWork);
            delete requestTask;
        },
        requestTask2, &requestTask2->asyncWork);
    napi_queue_async_work(env, requestTask2->asyncWork);
    return promise;
}

napi_value Http2Napi::End(napi_env env, napi_callback_info info) {
    LOGI("napi--->End");
    size_t argc = PARAM_COUNT_2;
    napi_value args[PARAM_COUNT_2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    std::string fdStr;
    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, fdStr);
    int fd = NapiUtil::StringToInt(fdStr);
    auto resIt = connectManagerMap.find(fd);
    if (resIt != connectManagerMap.end()) {
        LOGE("napi--->End fd %d", fd);
        if (sessionManagerMap.at(fd)) {
            return nullptr;
        }
        sessionManagerMap[fd] = 1;

        napi_deferred deferred;
        napi_value promise;
        napi_create_promise(env, &deferred, &promise);
        napi_value resourceName;
        napi_create_string_latin1(env, "end", NAPI_AUTO_LENGTH, &resourceName);
        Connection *connection = connectManagerMap.at(fd);
        Connection *connection2 = new Connection{.ssl = connection->ssl,
                                                 .fd = fd,
                                                 .ssl_ctx = connection->ssl_ctx,
                                                 .env = env,
                                                 .deferred = deferred,
                                                 .session = connection->session};
        napi_create_async_work(
            env, nullptr, resourceName,
            [](napi_env env, void *data) {
                Connection *connection = (Connection *)data;
                time_t startTime = time(NULL);
                while (streamCloseManagerMap.find(connection->fd) == streamCloseManagerMap.end()) {
                    for (const auto &child : streamCloseManagerMap) {
                        if (child.first == connection->fd) {
                            break;
                        }
                    }
                    LOGE("streamCloseManagerMap find %d", connection->fd);
                    usleep(100);
                    time_t currentTime = time(NULL);
                    // TODO 10秒之后如果没有检测到close 直接退出
                    if (timeDifference(startTime, currentTime) > 10) {
                        break;
                    }
                }
                //TODO 等待服务器彻底关闭
                sleep(3);
                if (freeManagerMap.find(connection->fd) != freeManagerMap.end()) {
                    LOGE("freeManagerMap find success");
                    return;
                }
                freeManagerMap[connection->fd] = true;
                if (connection->session != NULL) {
                    nghttp2_session_del(connection->session);
                }
                if (connection->ssl != NULL) {
                    SSL_shutdown(connection->ssl);
                    SSL_free(connection->ssl);
                }
                if (connection->ssl_ctx != NULL) {
                    SSL_CTX_free(connection->ssl_ctx);
                }
                shutdown(connection->fd, SHUT_WR);
                close(connection->fd);
            },
            [](napi_env env, napi_status status, void *data) {
                LOGE("napi-->End success");
                Connection *connection = (Connection *)data;
                napi_value result;
                napi_create_int32(env, connection->fd, &result);
                napi_resolve_deferred(connection->env, connection->deferred, result);
                napi_delete_async_work(env, connection->asyncWork);
            },
            connection2, &connection2->asyncWork);
        napi_queue_async_work(env, connection2->asyncWork);
        return promise;
    } else {
        LOGE("Error End");
    }
    return nullptr;
}


napi_value Http2Napi::DnsResolveTxt(napi_env env, napi_callback_info info) {
    LOGE("napi-->DnsResolveTxt");
    size_t argc = PARAM_COUNT_1;
    napi_value args[PARAM_COUNT_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    std::string host;
    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, host);
    struct sigaction act;
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &act, 0);
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);
    napi_value resourceName;
    napi_create_string_latin1(env, "DnsResolveTxt", NAPI_AUTO_LENGTH, &resourceName);
    RequestTask *requestTask = new RequestTask{.env = env, .asyncWork = nullptr, .deferred = deferred, .host = host};
    napi_create_async_work(
        env, nullptr, resourceName,
        [](napi_env env, void *data) {
            RequestTask *requestTask = (RequestTask *)data;
            auto result = resolveTxt(requestTask->host);
            for (std::vector<std::string>::iterator it = result.begin(); it != result.end(); ++it) {
                requestTask->dnsText.append(*it);
            }
        },
        [](napi_env env, napi_status status, void *data) {
            LOGE("napi-->DnsResolveTxt success");
            RequestTask *requestTask = (RequestTask *)data;
            napi_value result;
            napi_create_string_utf8(env, requestTask->dnsText.c_str(), NAPI_AUTO_LENGTH, &result);
            napi_resolve_deferred(requestTask->env, requestTask->deferred, result);
            napi_delete_async_work(env, requestTask->asyncWork);
            delete requestTask;
        },
        requestTask, &requestTask->asyncWork);
    napi_queue_async_work(env, requestTask->asyncWork);
    return promise;
}

napi_value Http2Napi::DnsLookup(napi_env env, napi_callback_info info) {
    LOGE("napi-->DnsLookup");
    size_t argc = PARAM_COUNT_1;
    napi_value args[PARAM_COUNT_1] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    std::string host;
    NapiUtil::JsValueToString(env, args[INDEX_0], STR_DEFAULT_SIZE, host);
    struct sigaction act;
    memset(&act, 0, sizeof(struct sigaction));
    act.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &act, 0);
    napi_deferred deferred;
    napi_value promise;
    napi_create_promise(env, &deferred, &promise);
    napi_value resourceName;
    napi_create_string_latin1(env, "DnsLookup", NAPI_AUTO_LENGTH, &resourceName);
    RequestTask *requestTask = new RequestTask{.env = env, .asyncWork = nullptr, .deferred = deferred, .host = host};
    napi_create_async_work(
        env, nullptr, resourceName,
        [](napi_env env, void *data) {
            RequestTask *requestTask = (RequestTask *)data;
            requestTask->dnsText = dnsLookupIPv4(requestTask->host);
        },
        [](napi_env env, napi_status status, void *data) {
            LOGE("napi-->DnsLookup success");
            RequestTask *requestTask = (RequestTask *)data;
            napi_value result;
            napi_create_string_utf8(env, requestTask->dnsText.c_str(), NAPI_AUTO_LENGTH, &result);
            napi_resolve_deferred(requestTask->env, requestTask->deferred, result);
            napi_delete_async_work(env, requestTask->asyncWork);
            delete requestTask;
        },
        requestTask, &requestTask->asyncWork);
    napi_queue_async_work(env, requestTask->asyncWork);
    return promise;
}

napi_value Http2Napi::OpenNativeLog(napi_env env, napi_callback_info info) 
{
    OHOS_LOG_ON = true;
    return nullptr;
}